// UserInteractionManager.h
//
#ifndef USERINTERACTIONMANAGER_ONCE
#define USERINTERACTIONMANAGER_ONCE

#include <map>
#include <string>
#include <iostream>

#include "MouseStatus.h"
#include "KeyboardStatus.h"

//#include <QKeyEvent>
//#include <QGraphicsSceneMouseEvent>
//#include <QGraphicsSceneWheelEvent>

#define NEWUIM 1

namespace QtFwk{

///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace {
	class UIMCallbackBase
	{
	public:
		virtual void invoke() = 0;
	};

	////
	template< typename T >
	class UIMCallback : public UIMCallbackBase
	{
	public:
		typedef void (T::*CallbackType)();
		T				*object;
		CallbackType	callback;

		void invoke() { (object->*callback)(); }
	};
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

class UIMCommand
{
private:
	std::string _name;
	std::string _activator;
	std::shared_ptr<UIMCallbackBase> _callback;

public:
	UIMCommand() {}
	
	template <typename T> 
	UIMCommand( const std::string &name, const std::string &activator, T *dest, void (T::*m)() )
	{
		std::shared_ptr<UIMCallback<T>> f(new UIMCallback<T>);
		f->object   = dest;
		f->callback = m;

		_callback  = f;
		_activator = activator;
		_name      = name;
	}

	std::string name()		const {return _name;}
	std::string activator() const {return _activator;}
	
	void execute() { if(_callback) _callback->invoke(); }	
};

////
class UserInteractionManager
{
private:
#if NEWUIM
	typedef std::map<unsigned int, UIMCommand> Bindings;
#else
	typedef std::map<std::string, UIMCommand> Bindings;
#endif
	
	Bindings keyBindings;
	Bindings mouseBindings;

	MouseStatus		ms;
	KeyboardStatus  kb;

#if NEWUIM
	// ------------------------------------------------------------------------------------------------
	void executeMouseCommand( unsigned int commandID )
	{
		Bindings::iterator it = mouseBindings.find( commandID );
		if( it != mouseBindings.end() )
			it->second.execute();
	}

	// ------------------------------------------------------------------------------------------------
	void executeKeyboardCommand( unsigned int commandID )
	{
		Bindings::iterator it = keyBindings.find( commandID );
		if( it != keyBindings.end() )
			it->second.execute();
	}
#else
	// ------------------------------------------------------------------------------------------------
	void executeMouseCommand( const std::string &commandID )
	{
		Bindings::iterator it = mouseBindings.find( commandID );
		if( it != mouseBindings.end() )
			it->second.execute();
	}

	// ------------------------------------------------------------------------------------------------
	void executeKeyboardCommand( const std::string &commandID )
	{
		Bindings::iterator it = keyBindings.find( commandID );
		if( it != keyBindings.end() )
			it->second.execute();
	}
#endif

	// ------------------------------------------------------------------------------------------------
	static std::map<std::string, unsigned int> mouseCommandCodes;
	static std::map<std::string, unsigned int> keyboardCommandCodes;
	static unsigned int decodeMouseCommnand( const std::string &command );
	static unsigned int decodeKeyboardCommnand( const std::string &command );
public:

	// ------------------------------------------------------------------------------------------------
	UserInteractionManager()	{ clear(); }
	~UserInteractionManager()	{ clear(); }

	// ------------------------------------------------------------------------------------------------
	bool registerMouseCommand( const UIMCommand &c )
	{
#if NEWUIM
		unsigned int commandCode = decodeMouseCommnand(c.activator());
		if( mouseBindings.find( commandCode ) != mouseBindings.end() )
			return false;	// bind already used! 
		mouseBindings[ commandCode ] = c;
#else

		if( mouseBindings.find( c.activator() ) != mouseBindings.end() )
			return false;	// bind already used! 
		mouseBindings[ c.activator() ] = c;
#endif

		return true;
	}
	
	// ------------------------------------------------------------------------------------------------
	bool registerKeyboardCommand( const UIMCommand &c )
	{
#if NEWUIM
		unsigned int commandCode = decodeKeyboardCommnand(c.activator()); //@todo
		if( keyBindings.find( commandCode ) != keyBindings.end() )
			return false;	// bind already used! 

		keyBindings[ commandCode ] = c;
#else
		if( keyBindings.find( c.activator() ) != keyBindings.end() )
			return false;	// bind already used! 

		keyBindings[ c.activator() ] = c;
#endif
		return true;
	}

	// ------------------------------------------------------------------------------------------------
	bool unregisterMouseCommand( const std::string &activator )
	{
#if NEWUIM
		unsigned int code = decodeMouseCommnand(activator);
		return mouseBindings.erase( code ) == 1;

#else
		return mouseBindings.erase( activator ) == 1;
#endif
	}

	// ------------------------------------------------------------------------------------------------
	bool unregisterKeyboardCommand( const std::string &activator )
	{
#if NEWUIM
		unsigned int code = decodeKeyboardCommnand(activator);
		return keyBindings.erase( code ) == 1;
#else
		return keyBindings.erase( activator ) == 1;
#endif
	}

	// ------------------------------------------------------------------------------------------------
	void clear()
	{
		ms.reset();
		kb.reset();

		mouseBindings.clear();
		keyBindings.clear();
	}
	
	// ------------------------------------------------------------------------------------------------
	void updateMouse( QGraphicsSceneMouseEvent *e, bool updateButtons )
	{
		ms.update( e, updateButtons );
#if NEWUIM
		executeMouseCommand( ms.getCode() );
#else
		executeMouseCommand( ms.toString() );
#endif
	}

	// ------------------------------------------------------------------------------------------------
	void updateMouse( QGraphicsSceneWheelEvent *e, bool updateButtons )
	{
		ms.update( e, updateButtons );

#if NEWUIM
		executeMouseCommand( ms.getCode() );
#else
		executeMouseCommand( ms.toString() );
#endif
	}	

	// ------------------------------------------------------------------------------------------------
	void updateKeyboard( QKeyEvent *e, bool keyPressed )
	{
		kb.update( e, keyPressed );
		//e->matches(
		////const int i = e->nativeVirtualKey();
		//
		//kb.fromString( e->text() );
		//if( i<256 )
		//	ks[i] = keyPressed;
		//
		//kmod = MouseStatus::ModifierNone;
		//if( e->modifiers() & Qt::AltModifier     ) kmod |= MouseStatus::ModifierAlt;
		//if( e->modifiers() & Qt::ShiftModifier	 ) kmod |= MouseStatus::ModifierShift;
		//if( e->modifiers() & Qt::ControlModifier ) kmod |= MouseStatus::ModifierCtrl;

#if NEWUIM
		if( keyPressed )
			executeKeyboardCommand( kb.getCode() );
#else
		executeKeyboardCommand( kb.toString() );
#endif
	}

	MouseStatus &mouseStatus() { return ms; }

	// ------------------------------------------------------------------------------------------------
	//bool load( const std::string &filename ) { return false; }
	// ------------------------------------------------------------------------------------------------
	//bool save( const std::string &filename ) { return false; }
};

}

#endif

